package com.demo.config;

import com.demo.config.lifecycle.DisruptorLifeCycleContainer;
import com.demo.memdb.ItemRepository;
import com.demo.request.*;
import com.lmax.disruptor.dsl.Disruptor;
import com.xjq.common.context.SpringContextHolder;
import com.xjq.common.exception.AppException;
import com.xjq.common.util.BeanUtil;
import lombok.Data;
import com.demo.StartupOrderConstants;
import com.demo.command.infras.CommandDispatcher;
import com.demo.command.infras.DefaultCommandDispatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.Executors;

@Configuration
@EnableConfigurationProperties({ RequestDisruptorConfiguration.DisruptorProperties.class })
public class RequestDisruptorConfiguration{

  @Autowired
  private DisruptorProperties disruptorProperties;

  @Bean
  public CommandDispatcher commandDispatcher() {
    return new DefaultCommandDispatcher();
  }

  @Bean
  public RequestDtoEventProducer requestDtoEventProducer() throws AppException {

    RequestDtoEventDbOutputer requestDtoEventDbOutputer = new RequestDtoEventDbOutputer();
    requestDtoEventDbOutputer.setCommandDispatcher(commandDispatcher());

    RequestDtoEventJmsOutputer requestDtoEventJmsOutputer = new RequestDtoEventJmsOutputer();
//    requestDtoEventJmsOutputer.setMessageSender(SpringContextHolder.getBean(JmsMessageSender.class));

    Disruptor<RequestDtoEvent> disruptor = new Disruptor<>(
        new RequestDtoEventFactory(),
        disruptorProperties.getJvmQueueSize(),
        Executors.defaultThreadFactory()
    );

    disruptor
        .handleEventsWith(requestDtoEventBusinessHandler())
        .then(requestDtoEventDbOutputer, requestDtoEventJmsOutputer)
        .then(new RequestDtoEventGcHandler());

    // disruptor 的异常处理是这样的,
    // 不论这种形式 A->B, 还是这种形式 A,B->C,D, 只有抛出异常的那个handler会中断执行
    disruptor.setDefaultExceptionHandler(new RequestDtoEventExceptionHandler());

    RequestDtoEventProducer requestDtoEventProducer = new RequestDtoEventProducer(disruptor.getRingBuffer());

    // 启动 disruptor
    BeanUtil.registerSingleton(
            SpringContextHolder.getContext(),
            "RequestDtoEventDisruptorLifeCycleContainer",
            new DisruptorLifeCycleContainer("RequestDtoEventDisruptor", disruptor,
            StartupOrderConstants.DISRUPTOR_REQUEST_DTO));

    return requestDtoEventProducer;
  }

  private RequestDtoEventBusinessHandler requestDtoEventBusinessHandler() {
    RequestDtoEventBusinessHandler requestDtoEventBusinessHandler = new RequestDtoEventBusinessHandler();
    requestDtoEventBusinessHandler.setItemRepository(SpringContextHolder.getBean(ItemRepository.class));
    return requestDtoEventBusinessHandler;
  }


  @ConfigurationProperties(prefix = "request-disruptor")
  @Data
  public static class DisruptorProperties {
    private int jvmQueueSize;
  }

}
